{
  type ReplaceStr<
    Str extends string,
    From extends string,
    To extends string,
  > = Str extends `${infer Prefix}${From}${infer Suffix}` ? `${Prefix}${To}${Suffix}` : Str;

  type res = ReplaceStr<'abc ??? dd', '?', '!'>;
}

{
  // ReplaceAll 将出现在字符串中的指定项全部替换

  type ReplaceAll<
    Str extends string,
    From extends string,
    To extends string,
  > = Str extends `${infer Left}${From}${infer Right}`
    ? `${Left}${To}${ReplaceAll<Right, From, To>}`
    : Str;

  type res = ReplaceAll<'?abc ??? dd', '?', '!'>;

  // 去除空格
  type res2 = ReplaceAll<'  ?abc ??? dd  ', ' ', ''>;

  /*
    类型参数 Str 是待处理的字符串类型，From 是待替换的字符，To 是替换到的字符。
    通过模式匹配提取 From 左右的字符串到 infer 声明的局部变量 Left 和 Right 里。
    用 Left 和 To 构造新的字符串，剩余的 Right 部分继续递归的替换。
    结束条件是不再满足模式匹配，也就是没有要替换的元素，这时就直接返回字符串 Str。
    这样就实现了任意数量的字符串替换：
  */
}

{
  // StringToUnion
  // 我们想把字符串字面量类型的每个字符都提取出来组成联合类型，也就是把 'dong' 转为 'd' | 'o' | 'n' | 'g'。

  // 很明显也是提取加构造

  type StringToUnion<Str extends string> = Str extends `${infer A}${infer B}${infer C}${infer D}`
    ? A | B | C | D
    : never;

  type res = StringToUnion<'dong'>;

  // 当字符串不确定长度时，数量不确定，在类型体操中就要条件反射的想到递归。

  type StringToUnion2<Str extends string> = Str extends `${infer A}${infer Rest}`
    ? A | StringToUnion2<Rest>
    : never;

  type res2 = StringToUnion2<'dong11aa'>;

  /*
    类型参数 Str 为待处理的字符串类型，通过 extends 约束为 string。
    通过模式匹配提取第一个字符到 infer 声明的局部变量 First，其余的字符放到局部变量 Rest。
    用 First 构造联合类型，剩余的元素递归的取。
    这样就完成了不确定长度的字符串的提取和联合类型的构造：
  */
}

{
  // ReverseStr 翻转字符串

  type ReverseStr<
    Str extends string,
    Result extends string = '',
  > = Str extends `${infer First}${infer Rest}` ? ReverseStr<Rest, `${First}${Result}`> : Result;

  type res = ReverseStr<'abcd'>;

  /*
    类型参数 Str 为待处理的字符串。类型参数 Result 为构造出的字符，默认值是空串。
    通过模式匹配提取第一个字符到 infer 声明的局部变量 First，其余字符放到 Rest。
    用 First 和之前的 Result 构造成新的字符串，把 First 放到前面，因为递归是从左到右处理，那么不断往前插就是把右边的放到了左边，完成了反转的效果。
    直到模式匹配不满足，就处理完了所有的字符。
  */
}

{
  // ReverseStr 这样也可以

  type ReverseStr<Str extends string> = Str extends `${infer First}${infer Rest}`
    ? `${ReverseStr<Rest>}${First}`
    : Str;

  type res = ReverseStr<'abcd'>;
}
